using System;
using System.Text;

namespace qip_history_synchronizer.Core
{
    /// <summary>
    /// String reader.
    /// </summary>
    public class StringReader
    {
        private string m_OriginalString = "";
        private string m_SourceString   = "";

        /// <summary>
        /// Default constructor.
        /// </summary>
        /// <param name="source">Source string.</param>
        public StringReader(string source)
        {	
            m_OriginalString = source;
            m_SourceString   = source;
        }

        // SYNC JAVA
        #region method AppenString

        /// <summary>
        /// Appends specified string to SourceString.
        /// </summary>
        /// <param name="str">String value to append.</param>
        public void AppenString(string str)
        {
            m_SourceString += str;
        }

        #endregion


        #region method ReadToFirstChar

        /// <summary>
        /// Reads to first char, skips SP,VTAB,HTAB,CR,LF from the beginning of source string.
        /// </summary>
        public void ReadToFirstChar()
        {            
            m_SourceString = m_SourceString.TrimStart();
        }

        #endregion

        //	public string ReadToDelimiter(char delimiter)
        //	{
        //	}

        #region method ReadSpecifiedLength

        /// <summary>
        /// Reads string with specified length. Throws exception if read length is bigger than source string length.
        /// </summary>
        /// <param name="length">Number of chars to read.</param>
        /// <returns></returns>
        public string ReadSpecifiedLength(int length)
        {
            if(m_SourceString.Length >= length){
                string retVal = m_SourceString.Substring(0,length);
                m_SourceString = m_SourceString.Substring(length);

                return retVal;
            }
            else{
                throw new Exception("Read length can't be bigger than source string !");
            }
        }

        #endregion
        // sync java
        #region method QuotedReadToDelimiter

        /// <summary>
        /// Reads string to specified delimiter or to end of underlying string. Notes: Delimiter in quoted string is skipped.
        /// Delimiter is removed by default.
        /// For example: delimiter = ',', text = '"aaaa,eee",qqqq' - then result is '"aaaa,eee"'.
        /// </summary>
        /// <param name="delimiter">Data delimiter.</param>
        /// <returns></returns>
        public string QuotedReadToDelimiter(char delimiter)
        {
            return QuotedReadToDelimiter(new char[]{delimiter});
        }

        /// <summary>
        /// Reads string to specified delimiter or to end of underlying string. Notes: Delimiters in quoted string is skipped.
        /// Delimiter is removed by default.
        /// For example: delimiter = ',', text = '"aaaa,eee",qqqq' - then result is '"aaaa,eee"'.
        /// </summary>
        /// <param name="delimiters">Data delimiters.</param>
        /// <returns></returns>
        public string QuotedReadToDelimiter(char[] delimiters)
        {
            return QuotedReadToDelimiter(delimiters,true);
        }

        /// <summary>
        /// Reads string to specified delimiter or to end of underlying string. Notes: Delimiters in quoted string is skipped. 
        /// For example: delimiter = ',', text = '"aaaa,eee",qqqq' - then result is '"aaaa,eee"'.
        /// </summary>
        /// <param name="delimiters">Data delimiters.</param>
        /// <param name="removeDelimiter">Specifies if delimiter is removed from underlying string.</param>
        /// <returns></returns>
        public string QuotedReadToDelimiter(char[] delimiters,bool removeDelimiter)
        {
            StringBuilder currentSplitBuffer = new StringBuilder(); // Holds active
            bool          inQuotedString     = false;               // Holds flag if position is quoted string or not
            char          lastChar           = (char)0;

            for(int i=0;i<m_SourceString.Length;i++){
                char c = m_SourceString[i];

                // Skip escaped(\) "
                if(lastChar != '\\' && c == '\"'){
                    // Start/end quoted string area
                    inQuotedString = !inQuotedString;
                }
			
                // See if char is delimiter
                bool isDelimiter = false;
                foreach(char delimiter in delimiters){
                    if(c == delimiter){
                        isDelimiter = true;
                        break;
                    }
                }

                // Current char is split char and it isn't in quoted string, do split
                if(!inQuotedString && isDelimiter){
                    string retVal = currentSplitBuffer.ToString();
                                        
                    // Remove readed string + delimiter from source string
                    if(removeDelimiter){
                        m_SourceString = m_SourceString.Substring(retVal.Length + 1);
                    }
                            // Remove readed string
                    else{
                        m_SourceString = m_SourceString.Substring(retVal.Length);
                    }

                    return retVal;
                }
                else{
                    currentSplitBuffer.Append(c);
                }

                lastChar = c;
            }

            // If we reached so far then we are end of string, return it
            m_SourceString = "";
            return currentSplitBuffer.ToString();
        }

        #endregion


        // SYNC JAVA
        #region method ReadWord

        /// <summary>
        /// Reads word from string. Returns null if no word is available.
        /// Word reading begins from first char, for example if SP"text", then space is trimmed.
        /// </summary>
        /// <returns></returns>
        public string ReadWord()
        {
            return ReadWord(true);
        }

        /// <summary>
        /// Reads word from string. Returns null if no word is available.
        /// Word reading begins from first char, for example if SP"text", then space is trimmed.
        /// </summary>
        /// <param name="unQuote">Specifies if quoted string word is unquoted.</param>
        /// <returns></returns>
        public string ReadWord(bool unQuote)
        {
            return ReadWord(unQuote,new char[]{' ',',',';','{','}','(',')','[',']','<','>','\r','\n'},false);
        }

        /// <summary>
        /// Reads word from string. Returns null if no word is available.
        /// Word reading begins from first char, for example if SP"text", then space is trimmed.
        /// </summary>
        /// <param name="unQuote">Specifies if quoted string word is unquoted.</param>
        /// <param name="wordTerminatorChars">Specifies chars what terminate word.</param>
        /// <param name="removeWordTerminator">Specifies if work terminator is removed.</param>
        /// <returns></returns>
        public string ReadWord(bool unQuote,char[] wordTerminatorChars,bool removeWordTerminator)
        {			
            // Always start word reading from first char.
            this.ReadToFirstChar();

            if(this.Available == 0){
                return null;
            }
            			            
            // quoted word can contain any char, " must be escaped with \
            // unqouted word can conatin any char except: SP VTAB HTAB,{}()[]<>

            if(m_SourceString.StartsWith("\"")){
                if(unQuote){
                    return TextUtils.UnQuoteString(QuotedReadToDelimiter(wordTerminatorChars,removeWordTerminator));
                }
                else{
                    return QuotedReadToDelimiter(wordTerminatorChars,removeWordTerminator);
                }				
            }
            else{
                int wordLength = 0;
                for(int i=0;i<m_SourceString.Length;i++){
                    char c = m_SourceString[i];

                    bool isTerminator = false;
                    foreach(char terminator in wordTerminatorChars){
                        if(c == terminator){
                            isTerminator = true;
                            break;
                        }
                    }
                    if(isTerminator){
                        break;
                    }

                    wordLength++;
                }
				
                string retVal = m_SourceString.Substring(0,wordLength);
                if(removeWordTerminator){
                    if(m_SourceString.Length >= wordLength + 1){
                        m_SourceString = m_SourceString.Substring(wordLength + 1);
                    }
                }
                else{
                    m_SourceString = m_SourceString.Substring(wordLength);
                }

                return retVal;
            }			
        }

        #endregion
        // SYNC JAVA	
        #region method ReadParenthesized

        /// <summary>
        /// Reads parenthesized value. Supports {},(),[],&lt;&gt; parenthesis. 
        /// Throws exception if there isn't parenthesized value or closing parenthesize is missing.
        /// </summary>
        /// <returns></returns>
        public string ReadParenthesized()
        {
            ReadToFirstChar();

            char startingChar = ' ';
            char closingChar  = ' ';

            if(m_SourceString.StartsWith("{")){
                startingChar = '{';
                closingChar = '}';
            }
            else if(m_SourceString.StartsWith("(")){
                startingChar = '(';
                closingChar = ')';
            }			
            else if(m_SourceString.StartsWith("[")){
                startingChar = '[';
                closingChar = ']';
            }						
            else if(m_SourceString.StartsWith("<")){
                startingChar = '<';
                closingChar = '>';
            }
            else{
                throw new Exception("No parenthesized value '" + m_SourceString + "' !");
            }

            bool inQuotedString = false; // Holds flag if position is quoted string or not
            char lastChar       = (char)0;

            int closingCharIndex = -1;
            int nestedStartingCharCounter = 0;
            for(int i=1;i<m_SourceString.Length;i++){
                // Skip escaped(\) "
                if(lastChar != '\\' && m_SourceString[i] == '\"'){
                    // Start/end quoted string area
                    inQuotedString = !inQuotedString;
                }
                        // We need to skip parenthesis in quoted string
                else if(!inQuotedString){
                    // There is nested parenthesis
                    if(m_SourceString[i] == startingChar){
                        nestedStartingCharCounter++;
                    }
                            // Closing char
                    else if(m_SourceString[i] == closingChar){
                        // There isn't nested parenthesis closing chars left, this is closing char what we want
                        if(nestedStartingCharCounter == 0){
                            closingCharIndex = i;
                            break;
                        }
                                // This is nested parenthesis closing char
                        else{
                            nestedStartingCharCounter--;
                        }
                    }
                }

                lastChar = m_SourceString[i];
            }

            if(closingCharIndex == -1){
                throw new Exception("There is no closing parenthesize for '" + m_SourceString + "' !");
            }
            else{
                string retVal = m_SourceString.Substring(1,closingCharIndex - 1);
                m_SourceString = m_SourceString.Substring(closingCharIndex + 1);

                return retVal;
            }
        }

        #endregion

        // SYNC JAVA

        #region method ReadToEnd

        /// <summary>
        /// Reads all remaining string, returns null if no chars left to read.
        /// </summary>
        /// <returns></returns>
        public string ReadToEnd()
        {
            if(this.Available == 0){
                return null;
            }

            string retVal = m_SourceString;
            m_SourceString = "";

            return retVal;
        }

        #endregion


        #region method StartsWith

        /// <summary>
        /// Gets if source string starts with specified value. Compare is case-sensitive.
        /// </summary>
        /// <param name="value">Start string value.</param>
        /// <returns></returns>
        public bool StartsWith(string value)
        {
            return m_SourceString.StartsWith(value);
        }
		
        /// <summary>
        /// Gets if source string starts with specified value.
        /// </summary>
        /// <param name="value">Start string value.</param>
        /// <param name="case_sensitive">Specifies if compare is case-sensitive.</param>
        /// <returns></returns>
        public bool StartsWith(string value,bool case_sensitive)
        {
            if(case_sensitive){
                return m_SourceString.StartsWith(value);
            }
            else{
                return m_SourceString.ToLower().StartsWith(value.ToLower());
            }
        }

        #endregion


        #region Properties Implementation

        /// <summary>
        /// Gets how many chars are available for reading.
        /// </summary>
        public long Available
        {
            get{ return m_SourceString.Length; }
        }

        /// <summary>
        /// Gets original string passed to class constructor.
        /// </summary>
        public string OriginalString
        {
            get{ return m_OriginalString; }
        }

        /// <summary>
        /// Gets currently remaining string.
        /// </summary>
        public string SourceString
        {
            get{ return m_SourceString; }
        }

        /// <summary>
        /// Gets position in original string.
        /// </summary>
        public int Position
        {
            get{ return m_OriginalString.Length - m_SourceString.Length; }
        }

        #endregion

    }
}